#include "StdAfx.h"
#include "DeviceInfo.h"

DeviceInfo_t::DeviceInfo_t() :
    winUSBHandle(NULL),
    deviceHandle(NULL),
    deviceSpeed(0),
    bulkInPipe(0),
    bulkOutPipe(0),
    interruptPipe(0),
    refCount(0)
{
    //InitializeSRWLock(&srwLock);
}

DeviceInfo_t::~DeviceInfo_t()
{
    Close(true);
}

BOOL DeviceInfo_t::Initialize(__in GUID* pInterfaceId)
{
    BOOL bResult;
    WINUSB_INTERFACE_HANDLE usbHandle;
    USB_INTERFACE_DESCRIPTOR ifaceDescriptor;
    WINUSB_PIPE_INFORMATION pipeInfo;
    UCHAR speed;
    ULONG length;
    
    deviceHandle = OpenDevice(pInterfaceId);
    bResult = WinUsb_Initialize(deviceHandle, &usbHandle);
    if (bResult)
    {
        winUSBHandle = usbHandle;
        length = sizeof(UCHAR);
        bResult = WinUsb_QueryDeviceInformation(
            winUSBHandle,
            DEVICE_SPEED,
            &length,
            &speed);
    }
    
    if (bResult)
    {
        deviceSpeed = speed;
        bResult = WinUsb_QueryInterfaceSettings(
            winUSBHandle,
            0,
            &ifaceDescriptor);
    }
    
    if (bResult)
    {
        for (int i = 0;i < ifaceDescriptor.bNumEndpoints; i++)
        {
            bResult = WinUsb_QueryPipe(
                winUSBHandle,
                0,
                (UCHAR)i,
                &pipeInfo);
            
            if (pipeInfo.PipeType == UsbdPipeTypeBulk &&
                USB_ENDPOINT_DIRECTION_IN(pipeInfo.PipeId))
            {
                bulkInPipe = pipeInfo.PipeId;
            }
            else if(pipeInfo.PipeType == UsbdPipeTypeBulk &&
                USB_ENDPOINT_DIRECTION_OUT(pipeInfo.PipeId))
            {
                bulkOutPipe = pipeInfo.PipeId;
            }
            else if(pipeInfo.PipeType == UsbdPipeTypeInterrupt)
            {
                interruptPipe = pipeInfo.PipeId;
            }
            else
            {
                bResult = FALSE;
                break;
            }
        }
    }

    if (bResult)
    {
        UCHAR val = FALSE;
        WinUsb_SetPipePolicy(winUSBHandle,
                             bulkInPipe,
                             RESET_PIPE_ON_RESUME,
                             sizeof(val),
                             (PVOID)&val);
        WinUsb_ResetPipe(winUSBHandle, bulkInPipe);
        WinUsb_ResetPipe(winUSBHandle, bulkOutPipe);
        WinUsb_FlushPipe(winUSBHandle, bulkInPipe);
    }

    return bResult == TRUE;
}

HANDLE DeviceInfo_t::OpenDevice(__in GUID* pInterfaceId)
{
    HANDLE hDev = NULL;
    WCHAR wszDevicePath[MAX_DEVPATH_LENGTH];
    BOOL retVal;
    GUID interfaceId = {0};
    DWORD errVal = 0;
  
    retVal = GetDevicePath(pInterfaceId,
        wszDevicePath,
        MAX_DEVPATH_LENGTH);
    if (TRUE == retVal)
    {
        hDev = CreateFile(wszDevicePath,
            GENERIC_WRITE | GENERIC_READ,
            FILE_SHARE_WRITE | FILE_SHARE_READ,
            NULL,
            OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
            NULL);
    }

    return hDev;
}

BOOL DeviceInfo_t::GetDevicePath(
    __in GUID* pDeviceInterface,
    __out_ecount(cchDevicePath) PWSTR wszDevicePath,
    size_t cchDevicePath)
{
    DWORD errVal = 0;
    BOOL bResult = FALSE;
    HDEVINFO hDevInfo;
    SP_DEVICE_INTERFACE_DATA interfaceData;
    PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = NULL;
    ULONG length;
    ULONG requiredLength=0;
    HRESULT hr;

    hDevInfo = SetupDiGetClassDevs(pDeviceInterface,
        NULL, NULL,
        DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
    if (INVALID_HANDLE_VALUE != hDevInfo)
    {
		if(cchDevicePath <= 0)
		{
			return FALSE;
		}

        interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
        
        bResult = SetupDiEnumDeviceInterfaces(hDevInfo,
            NULL,
            pDeviceInterface,
            0,
            &interfaceData);
		wszDevicePath[0] = NULL;
        if (TRUE == bResult)
        {
            bResult = SetupDiGetDeviceInterfaceDetail(hDevInfo,
                &interfaceData,
                NULL, 0,
                &requiredLength,
                NULL);
            if (FALSE == bResult)
            {
                detailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LMEM_FIXED, requiredLength);
                if (NULL == detailData)
                {
                    SetupDiDestroyDeviceInfoList(hDevInfo);
                    return FALSE;
                }
                
                detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
                length = requiredLength;
                
                bResult = SetupDiGetDeviceInterfaceDetail(hDevInfo,
                    &interfaceData,
                    detailData,
                    length,
                    &requiredLength,
                    NULL);
                if (TRUE == bResult)
                {
                    hr = StringCchCopy(wszDevicePath,
                        cchDevicePath,
                        detailData->DevicePath);
                    if (FAILED(hr))
                    {
                        bResult = FALSE;
                    }

                }
                
                LocalFree(detailData);
            }
        }
        else
        {
            errVal = GetLastError();
        }
        
        SetupDiDestroyDeviceInfoList(hDevInfo);
    }
    else
    {
        bResult = FALSE;
    }

    return bResult;
}

void DeviceInfo_t::Close(bool fullClose)
{
    WinUsb_AbortPipe(winUSBHandle, bulkInPipe);
    WinUsb_AbortPipe(winUSBHandle, bulkOutPipe);

    if (fullClose)
    {
        WinUsb_Free(winUSBHandle);
        CloseHandle(deviceHandle);
    }
}
